package Bunnies::InterListed;
use warnings;

use IO::Handle;
use Bunnies::QuoteCIBC qw(fetch);
use Data::Dumper;

my $DEFAULT_CONF = {
    INTERVAL => 0.2,
    MAX_USDCAD => 2,
    MIN_USDCAD => 0.5,
    MIN_PRICE => 3,
    SKIP_PRICE_RATIO => 0.618,
    MIN_VOLUME => 8000,
    TOP_COUNT => 20,
    TOP_PICK_FILE => '/tmp/top_pick',
    TOP_PICK_HTML => '/tmp/top_pick.html',
    AMOUNT_HINT1 => 10_000, #to archieve this amount (in CAD)
    AMOUNT_HINT2 => 50_000,
    AMOUNT_HINT3 => 100_000,
};

sub new {
    my ($CLASS, %options) = @_;
    my $self = { %options };

    foreach my $conf (keys %$DEFAULT_CONF) { #setup default conf
        $self->{$conf} ||= $DEFAULT_CONF->{$conf}; 
    }
    $self->{quote} ||= Bunnies::QuoteCIBC->new(); 
    
    #init top and bottom
    my ($top , $bottom) = ({}, {});
    ($self->{top_h}, $self->{top_l}) = (
        {KEY_0 => $self->{MIN_USDCAD}},
        {KEY_0 => $self->{MIN_USDCAD}}
    );
    ($self->{bottom_h}, $self->{bottom_l}) = (
        {KEY_0 => $self->{MAX_USDCAD}},
        {KEY_0 => $self->{MAX_USDCAD}}
    );
    for(my $i = 0; $i < $self->{TOP_COUNT}; $i++) {
        $top->{"KEY_$i"} = {CASymbol=> "KEY_$i", USDCAD=> $self->{MIN_USDCAD}};
        $bottom->{"KEY_$i"} = {CASymbol=> "KEY_$i", USDCAD=> $self->{MAX_USDCAD}};
    }
    $self->{top} = $top;
    $self->{bottom} = $bottom;

    #init file handle
    my $top_fh = IO::File->new("> $self->{TOP_PICK_FILE}") or die $!;
    #open(HTML_FH, '>', $self->{TOP_PICK_HTML}) or die $!;
    $top_fh->autoflush(1);
    #HTML_FH->autoflush(1);
    $self->{TOP_FH} = $top_fh;


    bless($self,$CLASS);
    return $self;
}

sub _errlog {
    my $fmt = shift;
    my $vlist = \@_;
    printf STDERR $fmt, @$vlist;
}

sub one_top_pick_line
{
    my ($self, $direction, $stock) = @_;
    my ($CAQ, $USQ) = ($stock->{CAQuote}, $stock->{USQuote});
    my ($USDCAD_expect, $short, $buy, $amt1, $amt2, $amt3);  

    my $USDCAD =  $CAQ->{last} / $USQ->{last};
    if($direction) { #higher rate, short at CA side and buy at US side 
        $USDCAD_expect = $CAQ->{bid}/ $USQ->{ask};
        $short = sprintf("short TSE:%6s:%6.2f", $stock->{CASymbol}, $CAQ->{bid});
        $buy =   sprintf("  buy  US:%6s:%6.2f", $stock->{USSymbol}, $USQ->{ask});

        $amt1 = $self->AMOUNT_HINT1 / $CAQ->{bid};
        $amt2 = $self->AMOUNT_HINT2 / $CAQ->{bid};
        $amt3 = $self->AMOUNT_HINT3 / $CAQ->{bid};
    } else {
        $USDCAD_expect = $CAQ->{ask} / $USQ->{bid};
        $short = sprintf("short  US:%6s:%6.2f",  $stock->{USSymbol}, $USQ->{bid});
        $buy =   sprintf("  buy TSE:%6s:%6.2f", $stock->{CASymbol}, $CAQ->{ask});

        $amt1 = $self->AMOUNT_HINT1 / $CAQ->{ask};
        $amt2 = $self->AMOUNT_HINT2 / $CAQ->{ask};
        $amt3 = $self->AMOUNT_HINT3 / $CAQ->{ask};
    }

    my $line = sprintf("TSE:%-6s US:%-6s%6.4f%6.4f%18d%18d%6.2f%6.2f%s%s%6d%6d%6d\n",
        $stock->{CASymbol}, $stock->{USSymbol},
        $USDCAD, $USDCAD_expect, $CAQ->{volume}, $USQ->{volume},
        $CAQ->{last}, $USQ->{last},
        $short, $buy, $amt1, $amt2, $amt3
    );

    return $line;
}

sub one_trace_line {
    my ($self, $hl, $s) = @_;

    my $hltxt;

    if($hl > 0) { 
        $hltxt = sprintf("sCA/bUS:%6.4f               ", $s->{USDCAD_hreal});
    } elsif($hl < 0) {
        $hltxt = sprintf("               sUS/bCA:%6.4f", $s->{USDCAD_lreal});
    } else {
        $hltxt = sprintf("                            ");
    }

    my $line = sprintf("TSE:%-6s  US:%-6s%6.4f %s " . 
        "%8d%8d blaCA:%6.2f%6.2f%6.2f blaUS:%6.2f%6.2f%6.2f",
        $s->{CASymbol}, $s->{USSymbol},
        $s->{USDCAD}, $hltxt, 
        $s->{CAQuote}->{volume}, $s->{USQuote}->{volume},
        $s->{CAQuote}->{bid}, $s->{CAQuote}->{last}, $s->{CAQuote}->{ask}, 
        $s->{USQuote}->{bid}, $s->{USQuote}->{last}, $s->{USQuote}->{ask}
    );
    return $line;
}

sub update_top {
    my ($self, $s) = @_;
    my $top = $self->{top};
    my $top_l = $self->{top_l};
    my $top_h = $self->{top_h};
    my $top_count = $self->{TOP_COUNT};
    my $old_top_l_key = (keys(%$top_l))[0];

    $self->{TOP_FH}->printf("%s", $s->{output_line}) if $s->{USDCAD} > (values(%$top_h))[0] ;
    delete $top_l->{$old_top_l_key};
    delete $top->{$old_top_l_key};
    $top->{$s->{CASymbol}} = $s;

    my @sorted = sort { $b->{USDCAD} <=> $a->{USDCAD} } values(%$top); 

    $self->{top_l}->{$sorted[$top_count - 1]->{CASymbol}} = $sorted[$top_count - 1]->{USDCAD};
    $self->{top_h}->{$sorted[0]->{CASymbol}} = $sorted[0]->{USDCAD};
}

sub update_bottom {
    my ($self, $s) = @_;
    my $bottom = $self->{bottom};
    my $bottom_h = $self->{bottom_h};
    my $bottom_l = $self->{bottom_l};
    my $old_bottom_h_key = (keys(%$bottom_h))[0];
    my $top_count = $self->{TOP_COUNT};

    $self->{TOP_FH}->printf("%s", $s->{output_line}) if $s->{USDCAD} < (values(%$bottom_l))[0];
    delete $bottom_h->{old_bottom_h_key};
    delete $bottom->{old_bottom_h_key};
    $bottom->{$s->{CASymbol}} = $s;

    my @sorted = sort { $a->{USDCAD} <=> $b->{USDCAD} } values(%$bottom); 

    $self->{bottom_h}->{$sorted[$top_count - 1]->{CASymbol}} = $sorted[$top_count - 1]->{USDCAD};
    $self->{bottom_l}->{$sorted[0]->{CASymbol}} = $sorted[0]->{USDCAD};
}

sub loop {
    my ($self, $stocks) = @_;

    my ($exchange, $response, $USSymbol, $url, $CAQuote, $USQuote, $USDCAD);
    my $count = 0;
    my $output_line;
    my ($prefix, $USDCAD_hreal, $USDCAD_lreal);
    my $total = (keys %$stocks);
    my $bottom = $self->{bottom};
    my $top_l = $self->{top_l};
    my $bottom_h = $self->{bottom_h};
    foreach my $stock (keys %$stocks) {
        $count ++;
        $USSymbol = $stocks->{$stock}{'USSymbol'};
        if($stock =~ m/\.U$/i){
            printf STDERR " %4d/%d", $count, $total;
            warn " TSE:$stock/US:$USSymbol: .U stock  skipped\n";
            next;
        }

        $CAQuote = $self->{quote}->fetch('T', $stock);
        $USQuote = $self->{quote}->fetch('US', $USSymbol);

        if(defined($CAQuote) and defined($USQuote)) {
            if($USQuote->{volume} <= $self->{MIN_VOLUME} or
               $CAQuote->{volume} <= $self->{MIN_VOLUME}) {
                _errlog(" %4d/%d %s\n", $count, $total, 
                 "TSE:$stock/US:$USSymbol: $CAQuote->{volume}/$USQuote->{volume} volume too small, skipped");
                delete $stocks->{$stock}; next;
            }
            if($USQuote->{last} < $self->{MIN_PRICE}) {
                _errlog(" %4d/%d %s\n", $count, $total,
                    "TSE:$stock/US:$USSymbol: $CAQuote->{last}/$USQuote->{last} price too low, skipped");
                delete $stocks->{$stock}; next;
            }
            if($CAQuote->{last} and $USQuote->{last}) {
                $USDCAD = $CAQuote->{last} / $USQuote->{last};
            } else {
                $USDCAD = 0;
            }

            if ($USDCAD > $self->{MAX_USDCAD} or $USDCAD < $self->{MIN_USDCAD}) {
                _errlog(" %4d/%d %s\n", $count, $total,
                     " Invalid USDCAD result: $USDCAD" .
                "\tTSE:$stock: $CAQuote->{last} $CAQuote->{volume}\n" . 
                "\tUS:$USSymbol: $USQuote->{last} $USQuote->{volume}\n");
                delete $stocks->{$stock}; next;
            }
            if($CAQuote->{bid} > 0.01 and $USQuote->{ask} > 0.01 and
                $CAQuote->{ask} > 0.01 and $USQuote->{bid} > 0.01) {
                $USDCAD_hreal = $CAQuote->{bid} / ($USQuote->{ask}); #short ca buy us
                $USDCAD_lreal = $CAQuote->{ask} / ($USQuote->{bid});#short us buy ca
            } else {
                $USDCAD_hreal = 0;
                $USDCAD_lreal = 0;
            }

            $stocks->{$stock} = {
                CASymbol => $stock,
                USSymbol => $USSymbol,
                CAQuote => $CAQuote,
                USQuote => $USQuote,
                USDCAD => $USDCAD,
                USDCAD_hreal => $USDCAD_hreal,
                USDCAD_lreal => $USDCAD_lreal,
            };
            #print Dumper($CAQuote);
            #print Dumper($USQuote);
            if($USDCAD > (values(%$top_l))[0]){
                $output_line = sprintf("+%4d/%d %s\n", $count, $total, 
                    $self->one_trace_line(1, $stocks->{$stock}));

                $stocks->{$stock}->{output_line} = $output_line;
                $self->update_top($stocks->{$stock});
            } elsif ($USDCAD < (values(%$bottom_h))[0]){
                $output_line = sprintf("-%4d/%d %s\n", $count, $total, 
                    $self->one_trace_line(-1, $stocks->{$stock}));

                $stocks->{$stock}->{output_line} = $output_line;
                $self->update_bottom($stocks->{$stock});
            } else {
                $output_line = sprintf(" %4d/%d %s\n", $count, $total, 
                    $self->one_trace_line(0, $stocks->{$stock}));
            }
        } else {
            printf STDERR " %4d/%d TSE:$stock/US:$USSymbol: No result  ", $count, $total;
            printf STDERR "%s\n", ($CAQuote ? $CAQuote->{last} : "N/A") . "/" .  ($USQuote ? $USQuote->{last} : "N/A");
            delete $stocks->{$stock}; next;
        }
        print STDERR $output_line;
        select(undef, undef, undef, $self->{INTERVAL});
            #last if ($count > 10);
    } #end of for
}
    



